package br3akr.DeadlyDinnerParty.Server.World;

import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;

import br3akr.DeadlyDinnerParty.Server.Actions.Action;
import br3akr.DeadlyDinnerParty.Server.Actions.ActionFactory;
import br3akr.DeadlyDinnerParty.Server.Actions.ActionResponse;
import br3akr.DeadlyDinnerParty.Server.Actions.AttackAction;
import br3akr.DeadlyDinnerParty.Server.Actions.BashAction;
import br3akr.DeadlyDinnerParty.Server.Actions.DescribeAction;
import br3akr.DeadlyDinnerParty.Server.Actions.DropAction;
import br3akr.DeadlyDinnerParty.Server.Actions.GagAction;
import br3akr.DeadlyDinnerParty.Server.Actions.GetAction;
import br3akr.DeadlyDinnerParty.Server.Actions.GiveAction;
import br3akr.DeadlyDinnerParty.Server.Actions.HandcuffAction;
import br3akr.DeadlyDinnerParty.Server.Actions.HideAction;
import br3akr.DeadlyDinnerParty.Server.Actions.ListenAction;
import br3akr.DeadlyDinnerParty.Server.Actions.MoveAction;
import br3akr.DeadlyDinnerParty.Server.Actions.MultiStepAction;
import br3akr.DeadlyDinnerParty.Server.Actions.OpenCloseAction;
import br3akr.DeadlyDinnerParty.Server.Actions.PickAction;
import br3akr.DeadlyDinnerParty.Server.Actions.PutAction;
import br3akr.DeadlyDinnerParty.Server.Actions.SearchAction;
import br3akr.DeadlyDinnerParty.Server.Actions.SpeakAction;
import br3akr.DeadlyDinnerParty.Server.Actions.StowAction;
import br3akr.DeadlyDinnerParty.Server.Engine.Server;
import br3akr.DeadlyDinnerParty.Server.Exceptions.DeadlyDinnerPartyException;


public class GameCharacter implements IsHidable {
	private String name;
	private String description;
	private Item heldItem;
	private Area location;
	private boolean recovering;
	private boolean dying;
	private GameCharacter.Status status;
	private Set<String> actionKeywords;
	private Action curAction;
	private Set<Item> inventory;
	private GameCharacter.Gender gender;
	private Area listeningToArea;
	private GameCharacter listeningToCharacter;
	private Set<GameCharacter> listeningCharacters;
	private Handcuffs handcuffs;
	private boolean gagged;
	private GameCharacter killer;
	private Weapon murderWeapon;
	private Set<GameCharacter> hiddenFromList;
	private boolean isHidden;
	private String hiddenDesc;
	
	public enum Gender {
		MALE, FEMALE
	}
	
	public enum Status {
		HEALTHY, HURT, INCAPACITATED, DEAD
	}
	
	public GameCharacter(String name, GameCharacter.Gender gender, String description, Set<String> keywords) {
		this.name = name;
		this.description = description;
		this.gender = gender;
		listeningCharacters = new HashSet<GameCharacter>();
		recovering = false;
		this.actionKeywords = new HashSet<String>();
		this.actionKeywords.addAll(MoveAction.keywords);
		this.actionKeywords.addAll(SpeakAction.keywords);
		this.actionKeywords.addAll(OpenCloseAction.keywords);
		this.actionKeywords.addAll(ListenAction.keywords);
		this.actionKeywords.addAll(BashAction.keywords);
		this.actionKeywords.addAll(DescribeAction.keywords);
		this.actionKeywords.addAll(AttackAction.keywords);
		this.actionKeywords.addAll(HandcuffAction.keywords);
		this.actionKeywords.addAll(GagAction.keywords);
		this.actionKeywords.addAll(DropAction.keywords);
		this.actionKeywords.addAll(GiveAction.keywords);
		this.actionKeywords.addAll(PutAction.keywords);
		this.actionKeywords.addAll(GetAction.keywords);
		this.actionKeywords.addAll(StowAction.keywords);
		this.actionKeywords.addAll(PickAction.keywords);
		this.actionKeywords.addAll(SearchAction.keywords);
		this.actionKeywords.addAll(HideAction.keywords);
		this.curAction = null;
		if (keywords != null) {
			this.actionKeywords.addAll(keywords);
		}
		inventory = new HashSet<Item>();
		status = Status.HEALTHY;
		handcuffs = null;
		hiddenFromList = new HashSet<GameCharacter>();
	}
	
	public void addListener(GameCharacter gc) {
		listeningCharacters.add(gc);
	}
	
	public void removeListener(GameCharacter gc) {
		listeningCharacters.remove(gc);
	}
	
	public Set<GameCharacter> getListeningCharacters() {
		return listeningCharacters;
	}
	
	public void setListenArea(Area area) throws DeadlyDinnerPartyException {
		
		listeningToArea = area;
	}
	
	public void listenCharacter(String name) throws DeadlyDinnerPartyException {
		GameCharacter listenToCharacter = location.getCharacter(name);
		
		if (name == null) {
			throw new DeadlyDinnerPartyException("A character must be supplied to listen to.");
		}
		if (listenToCharacter == null) {
			throw new DeadlyDinnerPartyException("There is no one here by the name of " + name + " to listen to.");
		}
		
		listeningToCharacter = listenToCharacter;
	}
	
	public Item getInventoryItem(String name) throws DeadlyDinnerPartyException {
		Item retval = null;
		
		if (heldItem != null && heldItem.getName().toUpperCase().startsWith(name.toUpperCase())) {
			retval = heldItem;
		} else {
			for (Item i : inventory) {
				if (i.getName().toUpperCase().equals(name.toUpperCase())) {
					retval = i;
					break;
				} else if (i.getName().toUpperCase().startsWith(name.toUpperCase())) {
					if (retval == null) {
						retval = i;
					} else {
						throw new DeadlyDinnerPartyException("Which item?");
					}
				}
			}
		}
		return retval;
	}
	
	public void stopListening() {
		listeningToArea = null;
		listeningToCharacter = null;
	}
	
	public GameCharacter getListeningToCharacter() {
		return listeningToCharacter;
	}
	
	public Area getListeningToArea() {
		return listeningToArea;
	}
	
	public Set<Item> getInventory() {
		return inventory;
	}
	
	public void addItem(Item item) {
		if (item != null) {
			inventory.add(item);
		}
	}
	
	public void removeItem(Item item) {
		if (item != null) {
			inventory.remove(item);
		}
		if (heldItem != null && heldItem.equals(item)) {
			heldItem = null;
		}
	}
	
	public Action takeAction(String action) {
		Action retval = null;
		StringTokenizer st = new StringTokenizer(action);
		String keyword = null;
		
		if (st.hasMoreTokens()) {
			keyword = st.nextToken();
			
			if (curAction != null) {
				if (curAction instanceof MultiStepAction) {
					if (((MultiStepAction)curAction).isComplete()) {
						curAction = null;
					}
				} else {
					curAction = null;
				}
			}
			if (curAction != null) {
				retval = curAction;
			}
			for (String checkKeyword : actionKeywords) {
				if (checkKeyword.equalsIgnoreCase(keyword)) {
					ActionFactory af = new ActionFactory();
					Action checkAction = af.getAction(keyword);
					if (checkAction.isFree() || retval == null) {
						retval = checkAction;
					}
					if (curAction == null) {
						curAction = retval;
					}
				}
			}
		}
		
		return retval;
	}
	
	public String getName() {
		return name;
	}
	
	public String getDescription() {
		return description;
	}

	public void wear(String itemName) throws DeadlyDinnerPartyException {
		
	}
	
	public void holdItem(Item item) throws DeadlyDinnerPartyException {
		heldItem = item;
	}
	
	public Item getHeldItem() {
		return heldItem;
	}
	
	public void setLocation(Area area) {
		this.location = area;
	}
	
	public Area getLocation() {
		return location;
	}
	
	public void setRecovering(boolean recovering) {
		this.recovering = recovering;
		if (!recovering) {
			ActionResponse actionResponse = new ActionResponse();
			actionResponse.addOnlooker(this, "You have recovered.");
			Server.world.broadcastAction(actionResponse);
		}
	}
	
	public boolean isRecovering() {
		return recovering;
	}
	
	public GameCharacter.Gender getGender() {
		return this.gender;
	}
	
	public void setGender(GameCharacter.Gender gender) {
		this.gender = gender;
	}
	
	public GameCharacter.Status getStatus() {
		return status;
	}
	
	public void setStatus(GameCharacter.Status st) {
		this.status = st;
	}
	
	public boolean isDying() {
		return dying;
	}
	
	public void setDying(boolean dying) {
		this.dying = dying;
	}
	
	public Handcuffs getHandcuffs() {
		return handcuffs;
	}
	
	public Handcuffs removeHandcuffs() {
		Handcuffs retval = handcuffs;
		handcuffs = null;
		return retval;
	}
	
	public void handcuff(Handcuffs hc) throws DeadlyDinnerPartyException {
		if (hc == null) {
			throw new DeadlyDinnerPartyException("No handcuffs supplied");
		}
		handcuffs = hc;
	}
	
	public boolean isGagged() {
		return gagged;
	}
	
	public void setGagged(boolean gagged) {
		this.gagged = gagged;
	}

	public GameCharacter getKiller() {
		return killer;
	}

	public void setKiller(GameCharacter killer) {
		this.killer = killer;
	}

	public Weapon getMurderWeapon() {
		return murderWeapon;
	}

	public void setMurderWeapon(Weapon murderWeapon) {
		this.murderWeapon = murderWeapon;
	}

	public void stopHiding() {
		hiddenFromList = new HashSet<GameCharacter>();
	}
	
	@Override
	public boolean isHiddenFrom(GameCharacter c) {
		boolean retval = false;
		
		if (c != null) {
			retval = hiddenFromList.contains(c);
		}
		return retval;
	}

	@Override
	public void addHiddenFrom(GameCharacter c) {
		hiddenFromList.add(c);
	}

	@Override
	public void removeHiddenFrom(GameCharacter c) {
		hiddenFromList.remove(c);
	}
	
	public Action getCurrentAction() {
		return curAction;
	}
	
	@Override
	public void hide(String hideDesc) {
		isHidden = true;
		hiddenDesc = hideDesc;
	}

	@Override
	public void unhide() {
		isHidden = false;
		hiddenDesc = null;
	}
	
	@Override
	public boolean isHidden() {
		return isHidden;
	}
	
	@Override
	public String getHiddenDescription() {
		return hiddenDesc;
	}
}
